#include <linux/init.h>
#include <linux/types.h>
#include <linux/idr.h>
#include <linux/input/mt.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/random.h>
#include <linux/major.h>
#include <linux/proc_fs.h>
#include <linux/sched.h>
#include <linux/seq_file.h>
#include <linux/poll.h>
#include <linux/device.h>
#include <linux/mutex.h>
#include <linux/rcupdate.h>
#include <linux/cdev.h>  
#include <linux/interrupt.h> 
#include <linux/ioctl.h>  
#include <linux/gpio.h>  
#define DEVICE_NAME "ioinput"              //设备名称
#define IRQNUM1      (IRQ_GPIO_START+0x0103)//中断号  //GPIOP_I3
//#define IRQNUM2      (IRQ_GPIO_START+0x0104)//中断号  //GPIO_PI4
#define IRQNUM2      (IRQ_GPIO_START+0x0105)//中断号  //GPIO_PI5
struct conf
{
  unsigned char sample_io;
  unsigned char sample_io_enable;
  unsigned char action_io;

};



int light_major = 200; 
static struct class *firstdrv_class;  
static struct device *firstdrv_device;  
static volatile int ev_press = 0;
static DECLARE_WAIT_QUEUE_HEAD(io_waitq);

struct ioput_data {
	struct cdev cdev;  
	int vaild;  //
        int cnt;//计数
};
struct ioput_data *myinput;
struct conf tps_conf={.sample_io=0,.sample_io_enable=0,.action_io=0};

static irqreturn_t ioinput_interrupt1(int irq, void *dev_id)
{
	struct ioput_data *io_irqs = (struct ioput_data *)dev_id;
	if(sample_io==1&&sample_io_enable==1)
	{
		if(gpio_get_value(NUC970_PI3)==1)//down - up
			io_irqs->vaild =1;
		else
		     io_irqs->vaild =2;//up -down
	}

	wake_up_interruptible(&io_waitq); /*唤醒等待队列*/
	ev_press = 1;

	//printk ("\tInterrupt11!\n");
	return IRQ_HANDLED;
}


static irqreturn_t ioinput_interrupt2(int irq, void *dev_id)
{
	struct ioput_data *io_irqs = (struct ioput_data *)dev_id;
	io_irqs->cnt =2;
	wake_up_interruptible(&io_waitq); /*唤醒等待队列*/
	ev_press = 2;

	//printk ("\tInterrupt222!\n");
	return IRQ_HANDLED;
}

static irqreturn_t ioinput_interrupt3(int irq, void *dev_id)
{
	struct ioput_data *io_irqs = (struct ioput_data *)dev_id;
	io_irqs->cnt =2;
	wake_up_interruptible(&io_waitq); /*唤醒等待队列*/
	ev_press = 3;

	//printk ("\tInterrupt333!\n");
	return IRQ_HANDLED;
}

static irqreturn_t ioinput_interrupt4(int irq, void *dev_id)
{
	struct ioput_data *io_irqs = (struct ioput_data *)dev_id;
	io_irqs->cnt =2;
	wake_up_interruptible(&io_waitq); /*唤醒等待队列*/
	ev_press = 4;

	//printk ("\tInterrupt444!\n");
	return IRQ_HANDLED;
}

static int ioinput_open(struct inode *inode, struct file *file)
{
    	int err = 0;
        // IRQF_TRIGGER_HIGH 0x00000004                  指定中断触发类型：高电平有效。新增加的标志 
	file->private_data = container_of(inode->i_cdev,struct ioput_data,cdev); 
        /*注册中断函数*/
	err = request_irq(IRQNUM1, ioinput_interrupt1, IRQF_TRIGGER_MASK, "ioinput", myinput);///* IRQF_TRIGGER_RISING     指定中断触发类型：上升沿有效*/
	if (err)
	{
		/*如果出错，释放已经注册的中断，并返回*/
		disable_irq(IRQNUM1);
		free_irq(IRQNUM1, myinput);
                printk("request irq IRQNUM1 %d failed\n",IRQNUM1);
			
		return -EBUSY;
	}

        /*注册中断函数*/
	err = request_irq(IRQNUM2, ioinput_interrupt2, IRQF_TRIGGER_RISING, "ioinput", myinput);/* IRQF_TRIGGER_RISING     指定中断触发类型：上升沿有效*/
	if (err)
	{
		/*如果出错，释放已经注册的中断，并返回*/
		disable_irq(IRQNUM2);
		free_irq(IRQNUM2, myinput);
		printk("request irq IRQNU2 %d failed\n",IRQNUM2);

		return -EBUSY;
	}





	 ev_press = 1;
	/*正常返回*/
	return 0;
}
static int ioinput_close(struct inode *inode, struct file *file)
{
    free_irq(IRQNUM1, myinput);
    free_irq(IRQNUM2, myinput);

    return 0;
}
static int ioinput_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
	unsigned long err;

	struct ioput_data *dev = filp->private_data; 

	if (!ev_press)
	{
		if (filp->f_flags & O_NONBLOCK)
		    /*当中断标识为0 时，并且该设备是以非阻塞方式打开时，返回*/
		    return -EAGAIN;
		else
		    /*当中断标识为0 时，并且该设备是以阻塞方式打开时，进入休眠状态，等待被唤醒*/
		    wait_event_interruptible(io_waitq, ev_press);
	}

	/*把中断标识清零*/
	//ev_press = 0;

	/*传递到用户空间*/
	err = copy_to_user(buff, (const void *)&(dev->vaild), sizeof(int)*2);

	printk("valid =%d cnt =%d \n",dev->vaild,dev->cnt);
        if(ev_press==1)
	{
		if(dev->vaild==2)
			dev->vaild  =0 ;
		
	}
	if(ev_press==2)
	{
		
		dev->cnt = 0;
		
	}
	ev_press = 0;


	return 2;
}
static int ioinput_write(struct file *filp,const char __user *buff, size_t count, loff_t *offp)
{
	unsigned long err;
	char buf[10];
	struct ioput_data *dev = filp->private_data; 
		
	err = copy_from_user(buf, buff, 2);
	/*设置到cnt*/
	dev->cnt = buf[0]+ buf[1]*256;

	return 2;
}
static unsigned int ioinput_poll( struct file *file, struct poll_table_struct *wait)
{
    unsigned int mask = 0;

    /*把调用poll 或者select 的进程挂入队列，以便被驱动程序唤醒*/
    poll_wait(file, &io_waitq, wait);

    if (ev_press)  //中断事件发生，这时有数据可读，在mask中标记是可读事件发生
        mask |= POLLIN | POLLRDNORM;

    return mask;
}
/*设备操作集*/
static struct file_operations dev_fops = {
    .owner = THIS_MODULE,
    .open = ioinput_open,
    .release = ioinput_close,
    .read = ioinput_read,
    .write = ioinput_write,
    .poll = ioinput_poll,
};

static void ioinput_setup_cdev(struct ioput_data *dev,int index)  
{  
    int err,devno = MKDEV(light_major,index);  
  
    cdev_init(&dev->cdev,&dev_fops);  
    dev->cdev.owner = THIS_MODULE;  
    dev->cdev.ops = &dev_fops;  
  
    err = cdev_add(&dev->cdev,devno,1);  
  
    if(err)  
    {  
        printk(KERN_NOTICE "Error %d adding LED%d",err,index);  
    }  
} 
int ioinput_init(void)  
{  
    int result=0;  

    printk("ioinput dev init... ..\n");
  
    dev_t dev = MKDEV(light_major,0);  
    
   


    if(light_major)  
    {  
          
        result = register_chrdev_region(dev,1,"io_input");  
	/* io_input */  
	firstdrv_class = class_create(THIS_MODULE, "ioinput");  

	/* 在ioinput类下创建设备，供应用程序打开设备*/  
	firstdrv_device = device_create(firstdrv_class, NULL, MKDEV(light_major, 0), NULL, DEVICE_NAME);
    }  
  
    if(result < 0)  
    {  
        return result;  
    }  
  
    myinput = kmalloc(sizeof(struct ioput_data),GFP_KERNEL);  
    if(!myinput)  
    {  
        result = - ENOMEM;  
        unregister_chrdev_region(dev,1);   
	return result;
    }  
    memset(myinput,0,sizeof(struct ioput_data));  
    ioinput_setup_cdev(myinput,0);  
    printk("ioinput dev init ok\n");
    return 0;  
} 

/*注销设备*/
static void __exit dev_exit(void)
{
	
   	printk("ioinput dev exit ....\n");
	cdev_del(&myinput->cdev);     
	kfree(myinput);  
	unregister_chrdev_region(MKDEV(light_major,0),1); 
	device_unregister(firstdrv_device);  //卸载类下的设备  
   	class_destroy(firstdrv_class);      //卸载类  
    	printk("ioinput dev exit ok\n");
}
module_init(ioinput_init); //模块初始化，仅当使用insmod/podprobe 命令加载时有用，如果设备不是通过模块方式加载，此处将不会被调用

module_exit(dev_exit); //卸载模块，当该设备通过模块方式加载后，可以通过rmmod 命令卸载，将调用此函数

MODULE_LICENSE("GPL");//版权信息

MODULE_AUTHOR("Madest Inc.");//作者名字


